<?php

namespace Plugins\AdminBasic\Http\Admin\Service\MakeCURDFiles;

use App\Exception\ApiMessageException;
use App\Http\Admin\Service\AbstractAdminService;
use App\Http\Admin\Service\Traits\DestroyServiceTrait;
use App\Http\Admin\Service\Traits\StoreServiceTrait;
use App\Http\Admin\Service\Traits\SwitchServiceTrait;
use App\Http\Admin\Service\Traits\UpdateServiceTrait;
use App\Util\EasySearch;
use Hyperf\Stringable\Str;
use Sc\Util\ClassFile\Components\ClassFileConstruction;
use Sc\Util\ClassFile\Components\FunctionParam;
use Sc\Util\ClassFile\Components\Method;
use Sc\Util\ClassFile\Components\Out\ValueOut;
use Sc\Util\ImitateAopProxy\AopProxyTrait;

/**
 * Class MakeService
 */
class MakeService extends AbstractMake
{
    public function make(): string
    {
        $service = new ClassFileConstruction(Str::studly($this->table) . 'Service');
        $service->setNamespace($this->getNamespace('Service'));
        $service->setDocBlock($this->tableComment);

        $this->addUse($service);
        $service->addTraits(AopProxyTrait::class);
        $service->addTraits(StoreServiceTrait::class);
        $service->addTraits(UpdateServiceTrait::class);
        $service->addTraits(DestroyServiceTrait::class);
        $service->addTraits(SwitchServiceTrait::class);

        $this->addGetModelMethod($service);
        $this->addListsMethod($service);

        return $this->makeFile($this->getFilename('Service'), $service->out());
    }

    private function addUse(ClassFileConstruction $service): void
    {
        $service->addUses(AopProxyTrait::class);
        $service->addUses(ApiMessageException::class);
        $service->addUses(AbstractAdminService::class);
        $service->addUses(DestroyServiceTrait::class);
        $service->addUses(StoreServiceTrait::class);
        $service->addUses(SwitchServiceTrait::class);
        $service->addUses(UpdateServiceTrait::class);
        $service->addUses(EasySearch::class);
        $service->addUses($this->getModel());
    }

    private function getModel(): string
    {
        $modelName = $this->getAddPrefixStr(Str::studly($this->dir), '\\') . '\\' . Str::studly($this->table);
        if ($this->isPlugins) {
            return "Plugins\\{$this->plugins}\\Models" . $modelName;
        }

        return "App\\Models" . $modelName;
    }

    private function addGetModelMethod(ClassFileConstruction $service): void
    {
        $method = new Method('getModel');
        $model  = $service->getAppropriateClassName($this->getModel());
        $method->setReturnType($model);
        $method->setDocBlockComment('获取模型');

        $method->addParameters(function () {
            $param = new FunctionParam('id');
            $param->setType('int');
            $param->setDefault(null);
            return $param;
        });

        $method->addCode("return \$id ? $model::find(\$id) :new $model();");

        $service->addMethods($method);
    }

    private function addListsMethod(ClassFileConstruction $service): void
    {
        $method = new Method('lists');
        $model  = $service->getAppropriateClassName($this->getModel());

        $method->setReturnType('array');
        $method->setDocBlockComment('获取列表');

        $fields = array_filter($this->fields, fn($field) => !empty($field['lists_type']));
        $fields = array_map(fn($field) => "{$this->table}.{$field}", array_column($fields, 'field'));

        $joins = [];
        foreach ($this->fields as $field) {
            if (empty($field['join_field'])) {
                continue;
            }
            $table = $tableAlias = $field['join_field']['table'];
            if ($table === $this->table) {
                $table .= ' as parent';
                $tableAlias = 'parent';
            }
            $fields[] = "{$tableAlias}.{$field['join_field']['label']} as {$tableAlias}_{$field['join_field']['label']}";
            $joins[]  = "\$query->leftJoin('{$table}', '{$tableAlias}.{$field['join_field']['key']}', '=', '{$this->table}.{$field["field"]}');";
        }

        $method->addCode(...[
            '$query = ' . $model . '::select(' . ValueOut::out($fields, 8) . ');',
            ...$joins,
            '',
            'return EasySearch::create($query)->getData();'
        ]);

        $service->addMethods($method);
    }
}